#include "../image.h"
#include "texture.h"

#include <stdlib.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>

#include <IL/il.h>
#include <IL/ilu.h>
#include <IL/ilut.h>
#include <GL/glew.h>

bool Texture::inited = false;
int Texture::mem_usage = 0;
std::map<std::string, Texture*> Texture::textures;

Texture* Texture::loadFileTexture(const char *name){

	std::map<std::string, Texture*>::iterator it = textures.find(name);
	Texture *tex;

	if(it == textures.end()){

		tex = new Texture(name);
		textures[name] = tex;

		tex->references = 1;

	}else{
		tex = textures[name];
		tex->references++;

	}

	return tex;

}

Texture* Texture::loadImageTexture(Image *im){

	// no space saving on these
	Texture *tex = new Texture(im);
	tex->references = 1;

	return tex;
}

Texture* Texture::loadCubeFileTexture(const char *faces[]){

	// create an id that is contains all the texture names
	// should work so that different cubemaps can have same textures in different order and such
	char name[512];
	sprintf(name, "%s%s%s%s%s%s", faces[0], faces[1], faces[2], faces[3], faces[4], faces[5]);


	std::map<std::string, Texture*>::iterator it = textures.find(name);
	Texture *tex;

	if(it == textures.end()){

		tex = new Texture(faces);
		textures[name] = tex;

		tex->references = 1;

	}else{
		tex = textures[name];
		tex->references++;

	}

	return tex;
}

Texture::Texture(const char *name) {

	init();

	ILuint ilID;
	ilGenImages(1, &ilID);
	ilBindImage(ilID);
	ILboolean success = ilLoadImage(name);
	bool mipMap = true;

	texType = GL_TEXTURE_2D;
	texId = 0;

	if (success) {
		success = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
		format = ilGetInteger(IL_IMAGE_FORMAT);
		width = ilGetInteger(IL_IMAGE_WIDTH);
		height = ilGetInteger(IL_IMAGE_HEIGHT);
		texPixelSize = ilGetInteger(IL_IMAGE_BYTES_PER_PIXEL);

		// times 1.333 for mipmaps
		mem_usage += width * height * texPixelSize * 1.333f;

		glGenTextures(1, &texId);
		glBindTexture(texType, texId);


		glTexParameterf( texType, GL_TEXTURE_WRAP_S, GL_REPEAT );
		glTexParameterf( texType, GL_TEXTURE_WRAP_T, GL_REPEAT );

		if(!mipMap){

			glTexParameteri(texType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			glTexParameteri(texType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

			glTexImage2D(texType, 0, texPixelSize, width,
				height, 0, format, GL_UNSIGNED_BYTE,
				ilGetData());

		}else{
			glTexParameteri(texType, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
			glTexParameteri(texType, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
			gluBuild2DMipmaps(texType, texPixelSize, width, height,
					format, GL_UNSIGNED_BYTE, ilGetData());
		}
	} else {
		printf("blir inge bild");
	}

	ilDeleteImages(1, &ilID);
}

Texture::Texture(Image *im){

	init();

	texType = GL_TEXTURE_2D;
	texId = 0;
	width = im->w;
	height = im->h;
	texPixelSize = sizeof(float) * im->d;

	// times 1.333 for mipmaps
	mem_usage += width * height * texPixelSize * 1.333f;

	glGenTextures(1, &texId);
	glBindTexture(texType, texId);


	glTexParameterf( texType, GL_TEXTURE_WRAP_S, GL_REPEAT );
	glTexParameterf( texType, GL_TEXTURE_WRAP_T, GL_REPEAT );

	unsigned format;
	if(im->d == 1)
		format = GL_LUMINANCE;
	if(im->d == 3)
		format = GL_RGB;
	if(im->d == 4)
		format = GL_RGBA;

	/*glTexParameteri(texType, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(texType, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	gluBuild2DMipmaps(texType, format, width, height,
			format, GL_FLOAT, im->getData());*/

	// needs fixing
	glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, im->getData() );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

}


Texture::Texture(const char *faces[]){

	init();


	texType = GL_TEXTURE_CUBE_MAP;
	glGenTextures(1, &texId);
	glBindTexture(texType, texId);


	glTexParameteri(texType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(texType, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(texType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(texType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(texType, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

	// Load Cube Map images
	for(int i = 0; i < 6; i++){

		ILuint ilID;
		ilGenImages(1, &ilID);
		ilBindImage(ilID);
		ILboolean success = ilLoadImage(faces[i]);

		if (success) {
			success = ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);
			format = ilGetInteger(IL_IMAGE_FORMAT);
			width = ilGetInteger(IL_IMAGE_WIDTH);
			height = ilGetInteger(IL_IMAGE_HEIGHT);
			texPixelSize = ilGetInteger(IL_IMAGE_BYTES_PER_PIXEL);

			// times 1.333 for mipmaps
			mem_usage += width * height * texPixelSize * 1.333f;

			// Load this texture map
			glTexParameteri(texType, GL_GENERATE_MIPMAP, GL_TRUE);

			glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, texPixelSize, width,
							height, 0, format, GL_UNSIGNED_BYTE,
							ilGetData());


		} else {
			printf("blir inge bild");
		}

		ilDeleteImages(1, &ilID);

	}

}

void Texture::init(){

	if(inited)
		return;

	ilInit();
	iluInit();
	//ilutRenderer(ILUT_OPENGL);
	//ilEnable(IL_ORIGIN_SET);
	//ilSetInteger(IL_ORIGIN_MODE, IL_ORIGIN_UPPER_LEFT);
	//ilutEnable(ILUT_GL_USE_S3TC);


	inited = true;
}

void Texture::saveBuffer(int width, int height, const char *name){

	init();

	ILuint ilID;

	std::remove(name);

	ilGenImages(1, &ilID);
	ilBindImage(ilID);
	ilSetInteger(IL_ORIGIN_LOWER_LEFT,IL_ORIGIN_MODE);

	ilTexImage(width, height, 1, 3, IL_RGB, IL_UNSIGNED_BYTE, NULL);


	glPixelStorei(GL_PACK_ALIGNMENT, 1);
	glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, ilGetData());

	ilSaveImage(name);

	ilDeleteImages(1, &ilID);
}

Texture::~Texture() {

	if (glIsTexture(texId))
		glDeleteTextures(1, &texId);

	int mod = 1;

	if(texType == GL_TEXTURE_CUBE_MAP) mod = 6;

	mem_usage -= width * height * texPixelSize * mod;


}

void Texture::clearAllTextures(){

	// clear from texture list
	std::map<std::string, Texture*>::iterator it;

	// show content:
	for ( it=textures.begin() ; it != textures.end(); it++ ){
		if((*it).second){


			delete (*it).second;

		}

	}

	textures.clear();
}

void Texture::clearTexture(Texture *tex){

	if(tex->references > 1){
		tex->references--;
		return;
	}



	// clear from texture list
	std::map<std::string, Texture*>::iterator it;

	// show content:
	for ( it=textures.begin() ; it != textures.end(); it++ ){
		if((*it).second == tex){
			textures.erase(it);
		}
	}

	delete tex;

}

